#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "oracle.h"
#include "fuzzykmeans.h"
#include <stdio.h>

#define re register int
#define in inline
#define maxn 900005
#define N    30425

int n, k, r;

double V[maxn] = {0, 0, 0, 1, 1, 0.5};
double C[maxn] = {-1, 0, 2, 1};

int main(int argc, char *argv[]) {
    int myid, numprocs;
    double coord_range[2] = {100005, -100005};
    double coord_rcv[2] = {0, 0};

    n = atoi(argv[1]), k = atoi(argv[2]), r = atoi(argv[3]);

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &myid);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);

    double start_tick = MPI_Wtime();

    //send max,min coordinates to every rank
    if (myid == 0)
        for (int i = 0; i < numprocs; i++) {
            if(i == 0)
                continue;
            MPI_Send(coord_range, sizeof(coord_range) / sizeof(coord_range[0]), MPI_DOUBLE, i, 0, MPI_COMM_WORLD);
        }

    if (myid != 0)
        MPI_Recv(coord_rcv, sizeof(coord_rcv) / sizeof(coord_rcv[0]), MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    else if(myid == 0){
        coord_rcv[0] = coord_range[0];
        coord_rcv[1] = coord_range[1];
    }


//    printf("rank:%d %lf %lf\n", myid, coord_rcv[0], coord_rcv[1]);

    srand(0);   //for same result, helpful to debug;
    oracle(V, 0, n);
    double mix, mxx, miy, mxy;
    mix = miy = coord_rcv[0], mxx = mxy = coord_rcv[1];
    re i;
    for (i = 0; i < 2 * n; i += 2)
        mix = fmin(mix, V[i]), mxx = fmax(mxx, V[i]), miy = fmin(miy, V[i + 1]), mxy = fmax(mxy, V[i + 1]);
    for (i = 0; i < 2 * k; i++) {
        C[i] = mix + (mxx - mix) * (rand() % N) / N;
        C[i + 1] = miy + (mxy - miy) * (rand() % N) / N;
    }

    int start_pos = n / numprocs * myid;
    int end_pos = start_pos + n / numprocs;
    if (myid == numprocs - 1)
        end_pos = n;

//    printf("rank:%d %d %d %d\n", myid, start_pos, end_pos, end_pos-start_pos);

    fuzzykmeans_mpi(V + start_pos * 2, end_pos - start_pos, C, k, myid, numprocs);

    double end_tick = MPI_Wtime();
    if (r == 0 && myid == 0) {
        printf("total rank: %d, n:%d, k:%d, program runtime of rank %d is %lf\n",
               numprocs, n, k, myid, end_tick - start_tick);
    } else if (r == 1) {
        int print_flag = 0;

        if (myid == 0) {
            //first rank print first then tell next rank to print.
            for (i = 0; i < 2 * (end_pos - start_pos); i++) {
                printf("%.2lf\n", (V + start_pos * 2)[i]);
            }
            if (myid < numprocs - 1)
                MPI_Send(&print_flag, 1, MPI_INT, myid + 1, 0, MPI_COMM_WORLD);
        } else {
            //recv from last rank then tell next rank to print.
            MPI_Recv(&print_flag, 1, MPI_INT, myid - 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
            for (i = 0; i < 2 * (end_pos - start_pos); i++) {
                printf("%.2lf\n", (V + start_pos * 2)[i]);
            }
            if (myid < numprocs - 1)
                MPI_Send(&print_flag, 1, MPI_INT, myid + 1, 0, MPI_COMM_WORLD);
        }

        //the last rank print
        if (myid == numprocs - 1) {
            printf("---------------data output done!---------------\n");
            for (i = 0; i < 2 * k; i++) printf("%.2lf\n", C[i]);
        }
    }else if(r == 2 && myid == 0) {
        printf("%d %lf\n", numprocs, end_tick - start_tick);
    }


    MPI_Finalize();

    return 0;


}


